package com.papers.juan.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.merge.LoopMergeStrategy;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.papers.common.annotation.Log;
import com.papers.common.annotation.RepeatSubmit;
import com.papers.common.constant.DictTypeConstants;
import com.papers.common.convert.ExcelBigNumberConvert;
import com.papers.common.core.controller.BaseController;
import com.papers.common.core.domain.PageQuery;
import com.papers.common.core.domain.R;
import com.papers.common.core.domain.entity.SysDictData;
import com.papers.common.core.page.TableDataInfo;
import com.papers.common.core.validate.AddGroup;
import com.papers.common.core.validate.EditGroup;
import com.papers.common.enums.BusinessType;
import com.papers.common.enums.YesOrNo;
import com.papers.common.excel.ExportPaperSelectSheetWriteHandler;
import com.papers.common.exception.ServiceException;
import com.papers.common.utils.BeanCopyUtils;
import com.papers.common.utils.file.FileUtils;
import com.papers.common.utils.poi.ExcelUtil;
import com.papers.juan.domain.TJuanPaper;
import com.papers.juan.domain.bean.PaperDetailBean;
import com.papers.juan.domain.bean.PaperExportBean;
import com.papers.juan.domain.bean.PaperImportBean;
import com.papers.juan.domain.bean.PaperStatisticsBean;
import com.papers.juan.domain.bean.QuestionInfoBean;
import com.papers.juan.domain.bean.RandomTakeOutPaperBean;
import com.papers.juan.domain.bo.TJuanPaperBo;
import com.papers.juan.domain.vo.TJuanPaperVo;
import com.papers.juan.service.ITJuanPaperService;
import com.papers.system.service.ISysDictDataService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 试卷
 *
 * @author papers
 * @date 2022-10-08
 */
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/juan/juanPaper")
public class TJuanPaperController extends BaseController {

    private final ITJuanPaperService iTJuanPaperService;

    private final ISysDictDataService iSysDictDataService;

    /**
     * 查询试卷列表
     */
    @SaCheckPermission("juan:juanPaper:list")
    @GetMapping("/list")
    public TableDataInfo<TJuanPaperVo> list(TJuanPaperBo bo, PageQuery pageQuery) {
        return iTJuanPaperService.queryPageList(bo, pageQuery);
    }

    /**
     * 查询已被抽取的试卷列表
     */
    @GetMapping("/queryBeenTakenOutPaperList")
    public TableDataInfo<TJuanPaperVo> queryBeenTakenOutPaperList(TJuanPaperBo bo, PageQuery pageQuery) {
        bo.setIsTakenOut(YesOrNo.YES.getKey());
        return iTJuanPaperService.queryPageList(bo, pageQuery);
    }

    /**
     * 查询未被抽取的试卷列表
     */
    @GetMapping("/queryUnBeenTakenOutPaperList")
    public TableDataInfo<TJuanPaperVo> queryUnBeenTakenOutPaperList(TJuanPaperBo bo, PageQuery pageQuery) {
        bo.setIsTakenOut(YesOrNo.NO.getKey());
        return iTJuanPaperService.queryPageList(bo, pageQuery);
    }

    /**
     * 导出试卷列表
     */
    @SaCheckPermission("juan:juanPaper:export")
    @Log(title = "试卷", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(TJuanPaperBo bo, HttpServletResponse response) {
        List<TJuanPaperVo> list = iTJuanPaperService.queryList(bo);
        ExcelUtil.exportExcel(list, "试卷", TJuanPaperVo.class, response);
    }

    /**
     * 获取试卷详细信息
     *
     * @param paperId 主键
     */
    @SaCheckPermission("juan:juanPaper:query")
    @GetMapping("/{paperId}")
    public R<TJuanPaperVo> getInfo(@NotNull(message = "主键不能为空")
                                   @PathVariable String paperId) {
        return R.ok(iTJuanPaperService.queryById(paperId));
    }

    /**
     * 新增试卷
     */
    @SaCheckPermission("juan:juanPaper:add")
    @Log(title = "试卷", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody TJuanPaperBo juanPaperBo) {
        return toAjax(iTJuanPaperService.insertByBo(juanPaperBo) ? 1 : 0);
    }

    /**
     * 修改试卷
     */
    @SaCheckPermission("juan:juanPaper:edit")
    @Log(title = "试卷", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody TJuanPaperBo bo) {
        return toAjax(iTJuanPaperService.updateByBo(bo) ? 1 : 0);
    }

    /**
     * 删除试卷
     *
     * @param paperIds 主键串
     */
    @SaCheckPermission("juan:juanPaper:remove")
    @Log(title = "试卷", businessType = BusinessType.DELETE)
    @DeleteMapping("/{paperIds}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空")
                          @PathVariable String[] paperIds) {
        return toAjax(iTJuanPaperService.deleteWithValidByIds(Arrays.asList(paperIds), true) ? 1 : 0);
    }

    /**
     * 根据试卷id列表获取试卷名称列表
     *
     * @param paperIds 试卷id列表
     * @return 试卷名称列表
     */
    @GetMapping("/queryPaperNamesByIds/{paperIds}")
    public R<List<String>> queryPaperNamesByIds(@NotEmpty(message = "主键不能为空")
                                                @PathVariable String[] paperIds) {
        return R.ok(iTJuanPaperService.queryPaperNamesByIds(Arrays.asList(paperIds)));
    }

    /**
     * 随机抽卷
     *
     * @param bean 随机抽取试卷bean
     * @return 试卷列表
     */
    @PostMapping("/randomTakeOutPaper")
    public R<List<TJuanPaper>> randomTakeOutPaper(@RequestBody RandomTakeOutPaperBean bean) {
        return R.ok(iTJuanPaperService.randomTakeOutPaper(bean));
    }

    /**
     * 根据试卷id获取试卷统计数据
     *
     * @param paperId 试卷id
     * @return 试卷统计数据
     */
    @GetMapping("/queryPaperStatisticsById/{paperId}")
    public R<PaperStatisticsBean> queryPaperStatisticsById(@NotEmpty(message = "主键不能为空") @PathVariable String paperId) {
        return R.ok(iTJuanPaperService.queryPaperStatisticsById(paperId));
    }

    /**
     * 导出试卷json信息
     *
     * @param response 响应
     * @param paperId  试卷id
     */
    @PostMapping("/exportPaperJson/{paperId}")
    public void exportPaperJson(HttpServletResponse response, @NotEmpty(message = "试卷id不能为空") @PathVariable String paperId) {
        iTJuanPaperService.exportPaperJson(paperId, response);
    }

    /**
     * 导入试卷json信息
     */
    @PostMapping("/importPaperJson")
    public R<Void> importPaperJson(@RequestPart("file") MultipartFile file) {
        if (null == file) {
            return R.fail("请选择一个文件");
        }
        iTJuanPaperService.importPaperJson(file);
        return R.ok();
    }

    /**
     * 导入试卷
     *
     * @param file 试卷文件
     */
    @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Void> importData(@RequestPart("file") MultipartFile file) {
        List<PaperImportBean> list;
        try {
            list = EasyExcel.read(file.getInputStream()).headRowNumber(3).head(PaperImportBean.class)
                .sheet().doReadSync();
        } catch (Exception e) {
            log.error("读取数据失败：{}", e.getMessage());
            throw new ServiceException("读取数据失败，请正确填写数据！");
        }

        if (0 == list.size()) {
            throw new ServiceException("导入失败，没有读取到导入数据！");
        }
        iTJuanPaperService.importData(list);
        return R.ok();
    }

    /**
     * 导出试卷
     *
     * @param response response
     * @param paperId  试卷id
     */
    @PostMapping("/exportData/{paperId}")
    public void exportData(HttpServletResponse response, @NotEmpty(message = "试卷id不能为空") @PathVariable String paperId)
        throws UnsupportedEncodingException {
        // 试卷详情
        PaperDetailBean paperDetail = iTJuanPaperService.queryPaperDetailById(paperId);
        // 题目列表
        List<QuestionInfoBean> questionInfoList = paperDetail.getQuestionInfoList();
        // 题目数量
        int questionNum = questionInfoList.size();
        List<PaperExportBean> list = Lists.newArrayList();
        for (int i = 0; i < questionInfoList.size(); i++) {
            PaperExportBean paperExportBean = new PaperExportBean();
            BeanCopyUtils.copy(questionInfoList.get(i), paperExportBean);
            if (i == 0) {
                BeanCopyUtils.copy(paperDetail, paperExportBean);
            }
            list.add(paperExportBean);
        }

        // 设置excel表头样式
        WriteSheet sheet = EasyExcel.writerSheet("试卷信息").head(PaperExportBean.class).sheetNo(1).build();
        // 设置excel表格样式
        ExcelWriter writer = null;
        try {
            if (1 == questionNum) {
                writer = EasyExcel.write(response.getOutputStream())
                    // 设置表头从第2行开始
//                    .useDefaultStyle(true).relativeHeadRowIndex(1)
                    // 设置下拉框
//                    .registerWriteHandler(new ExportPaperSelectSheetWriteHandler(getDropDownMap()))
                    .needHead(true).excelType(ExcelTypeEnum.XLSX).build();
            } else if (1 < questionNum) {
                writer = EasyExcel.write(response.getOutputStream())
                    // 设置表头从第2行开始
//                    .useDefaultStyle(true).relativeHeadRowIndex(1)
                    // 设置下拉框
//                    .registerWriteHandler(new ExportPaperSelectSheetWriteHandler(getDropDownMap()))
                    .needHead(true).excelType(ExcelTypeEnum.XLSX)
                    // 前12列的questionNum的行数进行合并
                    .registerWriteHandler(new LoopMergeStrategy(questionNum, 0))
                    .registerWriteHandler(new LoopMergeStrategy(questionNum, 1))
                    .registerWriteHandler(new LoopMergeStrategy(questionNum, 2))
                    .registerWriteHandler(new LoopMergeStrategy(questionNum, 3))
                    .registerWriteHandler(new LoopMergeStrategy(questionNum, 4))
                    .registerWriteHandler(new LoopMergeStrategy(questionNum, 5))
                    .registerWriteHandler(new LoopMergeStrategy(questionNum, 6))
                    .registerWriteHandler(new LoopMergeStrategy(questionNum, 7))
                    .registerWriteHandler(new LoopMergeStrategy(questionNum, 8))
                    .registerWriteHandler(new LoopMergeStrategy(questionNum, 9))
                    .registerWriteHandler(new LoopMergeStrategy(questionNum, 10))
                    .build();
            }
        } catch (IOException e) {
            log.error("导出数据失败：{}", e.getMessage());
            throw new ServiceException("导出数据失败！");
        }
        // 写入excel数据
        writer.write(list, sheet);
        // 通知浏览器以附件的形式下载处理，设置返回头要注意文件名有中文
        response.setHeader("Content-disposition", "attachment;filename=" + new String("试卷信息".getBytes("gb2312"),
            "ISO8859-1") + ".xlsx");
        response.setContentType("multipart/form-data");
        response.setCharacterEncoding("utf-8");
        writer.finish();
    }

    /**
     * 获取导入模板
     */
    @PostMapping("/importTemplate")
    public void importTemplate(HttpServletResponse response) {
        try {
            String filename = ExcelUtil.encodingFilename("试卷信息");
            response.reset();
            FileUtils.setAttachmentResponseHeader(response, filename);
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
            ServletOutputStream os = response.getOutputStream();
            ExcelWriterSheetBuilder builder = EasyExcel.write(os, PaperExportBean.class)
                .autoCloseStream(false)
                // 设置表头从第2行开始
                .useDefaultStyle(true).relativeHeadRowIndex(1)
                // 设置下拉框
                .registerWriteHandler(new ExportPaperSelectSheetWriteHandler(getDropDownMap()))
                // 自动适配
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                // 大数值自动转换 防止失真
                .registerConverter(new ExcelBigNumberConvert())
                .sheet("试卷信息");
            builder.doWrite(new ArrayList<>());
        } catch (IOException e) {
            throw new RuntimeException("导出Excel异常");
        }
    }

    /**
     * 获取下拉框map
     *
     * @return 下拉框map
     */
    private Map<Integer, List<String>> getDropDownMap() {
        // 选拉框map，key：excel列索引，从0开始, value：下拉列表
        Map<Integer, List<String>> dropDownMap = new HashMap<>();
        // 是否有密封线下拉值
        List<String> sealLineValues = Arrays.stream(YesOrNo.values()).map(YesOrNo::getValue).collect(Collectors.toList());
        // 年级列表
        List<String> grades = iSysDictDataService.selectDictDataByType(DictTypeConstants.GRADE)
            .stream().map(SysDictData::getDictLabel).collect(Collectors.toList());
        // 题目类型列表
        List<String> questionTypes = iSysDictDataService.selectDictDataByType(DictTypeConstants.QUESTION_TYPE).stream()
            .map(SysDictData::getDictLabel).collect(Collectors.toList());
        // 难度长度列表
        List<String> questionDifficultList = iSysDictDataService.selectDictDataByType(DictTypeConstants.DIFFICULTY)
            .stream().map(SysDictData::getDictLabel).collect(Collectors.toList());

        dropDownMap.put(4, sealLineValues);
        dropDownMap.put(11, grades);
        dropDownMap.put(12, questionTypes);
        dropDownMap.put(13, questionDifficultList);
        return dropDownMap;
    }

}
